#include "xlcomm/net/detail/event_loop_thread_pool.h"

#include "xlcomm/base/logger.h"

namespace xlcomm {
namespace net {

EventLoopThreadPool::EventLoopThreadPool(EventLoop* base_loop,
                                         const std::string& name)
    : base_loop_(base_loop), name_(name), started_(false), index_(0) {
  XLCOMM_ASSERT(base_loop_ != nullptr);
}

EventLoopThreadPool::~EventLoopThreadPool() {
  LOG_INFO << "EventLoopThreadPool '" << name_ << "' is stopped.";
  // Don't delete loop, it's stack variable
}

void EventLoopThreadPool::Start(int thread_num, const ThreadInitCallback& cb) {
  XLCOMM_ASSERT(thread_num >= 0);
  XLCOMM_ASSERT(base_loop_->IsInLoopThread());
  XLCOMM_ASSERT(!started_);
  LOG_INFO << "EventLoopThreadPool '" << name_ << "' start...";

  for (int i = 0; i < thread_num; ++i) {
    char buff[name_.size() + 32];
    snprintf(buff, sizeof(buff), "%s-%d", name_.c_str(), i + 1);
    EventLoopThread* thread = new EventLoopThread(buff, cb_);
    loop_threads_.push_back(std::unique_ptr<EventLoopThread>(thread));
    loops_.push_back(thread->StartLoop());
  }
  if (thread_num == 0 && cb_) {
    cb_(base_loop_);
  }
  started_ = true;
}

EventLoop* EventLoopThreadPool::GetNextLoop() {
  XLCOMM_ASSERT(base_loop_->IsInLoopThread());
  XLCOMM_ASSERT(started_);

  EventLoop* loop = base_loop_;
  if (!loops_.empty()) {
    loop = loops_[index_];
    ++index_;
    if (index_ >= loops_.size()) {
      index_ = 0;
    }
  }
  return loop;
}

EventLoop* EventLoopThreadPool::GetLoopForHash(uint64_t hash_code) {
  XLCOMM_ASSERT(base_loop_->IsInLoopThread());
  XLCOMM_ASSERT(started_);

  EventLoop* loop = base_loop_;
  if (!loops_.empty()) {
    loop = loops_[hash_code % loops_.size()];
  }
  return loop;
}

std::vector<EventLoop*> EventLoopThreadPool::GetAllLoops() {
  XLCOMM_ASSERT(base_loop_->IsInLoopThread());
  XLCOMM_ASSERT(started_);

  if (loops_.empty()) {
    return std::vector<EventLoop*>(1, base_loop_);
  } else {
    return loops_;
  }
}

}  // namespace net
}  // namespace xlcomm